package edu.hawaii.myisern.model;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Implements a singleton of two ArrayList instances (one for the header, one for the data).
 * The command classes manipulate these singletons in response to user input.
 * The MyIsernModel manipulates string objects only.
 * To prevent multi-threaded access from corrupting the stack, all operations on the
 * singleton MyIsernModel instance are synchronized.
 *
 * @author Jitender Miglani
 * @author Philip Johnson
 * @author Ben Karsin
 */
public class MyIsernModel {
  static final String baseFolder = System.getProperty("user.home") + "/.myisern/";
  
  /** The singleton MyIsernModel instance. */
  private static MyIsernModel theInstance = new MyIsernModel();

  /** The internal ArrayList implementations. */
  private List<String> header;
  private List<List<String>> data;

  private ResearcherData researcherData;
  private CollaborationData collaborationData;
  private OrganizationData organizationData;
  
  private XmlSemanticChecker xmlChecker;

  /** Private constructor used to create a single instance of stack. */
  private MyIsernModel() {
    researcherData = new ResearcherData();
    researcherData.loadData(baseFolder);
    
    collaborationData = new CollaborationData();
    collaborationData.loadData(baseFolder);

    organizationData = new OrganizationData();
    organizationData.loadData(baseFolder);

    this.header = organizationData.getHeader();
    this.data = organizationData.getData();
    
    xmlChecker = new XmlSemanticChecker();
    xmlChecker.setCollaborationList(collaborationData.getCollaborations().getCollaboration());
    xmlChecker.setOrganizationList(organizationData.getOrganizations().getOrganization());
    xmlChecker.setResearcherList(researcherData.getResearchers().getResearcher());
  }

  /**
   * Checks for semantic errors in the XML data.
   * 
   * @return List of errors.
   */
  public synchronized List<String> getSemanticErrors() {
    List<String> result;
    xmlChecker.checkAll();
    
    result = xmlChecker.getErrorList();
    return result;
  }
  
  /**
   * Retrieves the Researcher data.
   * 
   * @return Researcher data object.
   */
  public static synchronized ResearcherData getResearcherData() {
    return theInstance.researcherData;
  }

  /**
   * Retrieves the Collaboration data.
   * 
   * @return Collaboration data object.
   */
  public static synchronized CollaborationData getCollaborationData() {
    return theInstance.collaborationData;
  }

  /**
   * Retrieves the Organization data.
   * 
   * @return Organization data object.
   */
  public static synchronized OrganizationData getOrganizationData() {
    return theInstance.organizationData;
  }

  /**
   * Retrieves the folder where data is being read from.
   * 
   * @return Folder name.
   */
  public static synchronized String getBaseFolder() {
    return baseFolder;
  }
  
  
  /**
   * Get the single instance of MyIsernModel object.
   *
   * @return A MyIsernModel.
   */
  public static synchronized MyIsernModel getInstance() {
    return MyIsernModel.theInstance;
  }


  /**
   * Adds an element to the header list.
   *
   * @param element the element to be added to the header list.
   */
  public synchronized void addHeader(String element) {
    header.add(element);
  }
  
  /**
   * Adds a list (row) to the data list.
   *
   * @param newRow the ArrayList to be added to the data list.
   */
  public synchronized void addData(List<String> newRow) {
    data.add(newRow);
  }
  
  /**
   * Returns true if the data list contains no elements. 
   * 
   * @return True if empty.
   */
  public synchronized boolean isEmpty() {
    return data.isEmpty();
  }

  /**
   * Gets the iterator attribute of the header list.
   *
   * @return The iterator value.
   */
  public synchronized Iterator<String> headerIterator() {
    return this.header.iterator();
  }
  
  /**
   * Gets the iterator attribute of the data list.
   *
   * @return The iterator value.
   */
  public synchronized Iterator<List<String>> dataIterator() {
    return this.data.iterator();
  }
  
  /**
   * Saves all the data to their respective XML files.
   */
  public synchronized void saveData() {
   this.researcherData.saveData(baseFolder); 
   this.collaborationData.saveData(baseFolder);
   this.organizationData.saveData(baseFolder);
  }
  
  /**
   * Converts a string in an (array).toString() form to a list of String objects.
   * 
   * @param val String to be converted
   * @return List of strings.
   */
  public static List<String> stringToArray(String val) {
   List<String> result = new ArrayList<String>();
   if (val.trim().isEmpty()) {
     return result;
   }
   
   String[] tmp = val.split(",");
   if (tmp == null) {
     result.add(val);
     return result;
   }
   
   if (tmp[0].charAt(0) == '[') {
     tmp[0] = tmp[0].substring(1);
   }
   if (tmp[tmp.length - 1].charAt(tmp[tmp.length - 1].length() - 1) == ']') {
     tmp[tmp.length - 1] = tmp[tmp.length - 1].substring(0, tmp[tmp.length - 1].length() - 1);
   }
   
   for (String item : tmp) {
     result.add(item.trim());
   }
   
   return result;
  }
  
  /**
   * Edits or adds an entry of the given type to the corresponding arrayList. 
   * @param newRow New entry to be added or edited.
   * @param pageType Either R, C, or O, depending on type of row.
   * @param add true if new entry to be added, false if edit.
   * @return String containing error message, if any.
   */
  public String addEntry(List<String> newRow, char pageType, boolean add) {
    String errorMessage = "";
    switch (pageType) {
    case 'R':
      errorMessage = this.researcherData.updateData(newRow, add);
      break;
    case 'C':
      errorMessage = this.collaborationData.updateData(newRow, add);
      break;
    case 'O':
      errorMessage = this.organizationData.updateData(newRow, add);
      break;
    default:
      return "Invalid pageType.";
    }

    if (errorMessage.length() == 0) {
      this.saveData();
    }
      
    return errorMessage;

  }
  
  /**
   * Converts null strings to empty strings.
   * @param test the string to be converted if null.
   * @return The same string, but is now empty if it was null.
   */
  public static String nullToEmpty(String test) {
    String temp = test;
    if (temp == null || temp == "") {
      temp = " ";
    }
    temp = temp.replace('[', ' ');
    temp = temp.replace(']', ' ');
    
    /*
    if (temp.charAt(0) == '[') {
      temp = temp.replace('[', ' ');
    if (temp.length() > 1) {
      
      if (temp.charAt(temp.length()-1) == ']') {
        temp = temp.substring(0, temp.length()-1);
      }
    }
    */
    if (temp == "") {
      temp = " ";
    }
    
    return temp;
  }
  
}
